import BpmnModeler from 'bpmn-js/lib/Modeler.js';

import customTranslate from './modules/translate/customTranslate.js';

import ContentPadModule from './modules/content-pad/index.js';
import PaletteModule from './modules/palette/index.js';

import {
  BpmnPropertiesPanelModule,
  BpmnPropertiesProviderModule,
  CamundaPlatformPropertiesProviderModule
} from 'bpmn-js-properties-panel';
// import camundaModdleExtension from "./modules/camunda-extension/index.js";

import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda.json';
import { uuid } from '@/views/page-config/flow/utils';

export const TaskName: { [key: string]: string } = {
  process: 'bpmn:Process',
  collabor: 'bpmn:Collaboration',
  user: 'bpmn:UserTask',
  script: 'bpmn:ScriptTask',
  receive: 'bpmn:ReceiveTask',
  start: 'bpmn:StartEvent',
  subProcess: 'bpmn:SubProcess',
  sequence: 'bpmn:SequenceFlow',
  message: 'bpmn:Message',
  signal: 'bpmn:Signal',
  formalExpr: 'bpmn:FormalExpression',
  timeEvent: 'bpmn:TimerEventDefinition',
  extensionEl: 'bpmn:ExtensionElements',
  standardLoop: 'bpmn:StandardLoopCharacteristics',
  multiLoop: 'bpmn:MultiInstanceLoopCharacteristics',
  document: 'bpmn:Documentation'
};
export const CamundaTaskName: { [key: string]: string } = {
  failedRetryTime: 'camunda:FailedJobRetryTimeCycle',
  field: 'camunda:Field',
  taskListener: 'camunda:TaskListener',
  exListener: 'camunda:ExecutionListener',
  script: 'camunda:Script',
  properties: 'camunda:Properties',
  property: 'camunda:Property'
};
const ElementEvents: { [key: string]: string } = {
  click: 'element.click',
  changed: 'element.changed'
};
export const ModelerEvents: { [key: string]: string } = {
  importE: 'import.done',
  selectionChanged: 'selection.changed',
  addShape: 'shape.added',
  removeShape: 'shape.removed',
  moveShapeS: '',
  moveShapeE: 'shape.move.end',
  connectE: 'connect.end',
  moveConnect: 'connect.move',
  stackChanged: 'commandStack.changed'
};
class FlowModeler {
  private modeler: any = null;

  constructor() {}
  init(dom: any, customPropertiesPanel: boolean) {
    const opts: any = {
      container: dom,
      additionalModules: [
        ContentPadModule,
        //自定义左侧栏
        PaletteModule,
        // 国际化
        customTranslate()
      ],
      moddleExtensions: {
        // camunda的属性定义
        camunda: camundaModdleDescriptor
      }
    };
    // 使用内置右侧属性栏
    if (!customPropertiesPanel) {
      //添加内置右侧属性栏
      opts.propertiesPanel = {
        parent: '#js-properties-panel'
      };
      // 添加右侧属性栏相关
      opts.additionalModules.push(
        ...[
          //右边的属性栏（自定义）
          // xxx
          // 右边的属性栏(camunda官方提供)
          BpmnPropertiesPanelModule,
          BpmnPropertiesProviderModule,
          CamundaPlatformPropertiesProviderModule
        ]
      );
    }
    this.modeler = new BpmnModeler(opts);
  }
  private activeElement: any = null;
  setActiveElement(activeElement: any) {
    this.activeElement = activeElement;
  }
  getActiveElement() {
    return this.activeElement;
  }
  getModeler() {
    return this.modeler;
  }
  getBpmnFactory() {
    return this.modeler.get('bpmnFactory');
  }
  getElementFactory() {
    return this.modeler.get('elementFactory');
  }
  getElementRegistry() {
    return this.modeler.get('elementRegistry');
  }
  getModeling() {
    return this.modeler.get('modeling');
  }
  getModdle() {
    return this.modeler.get('moddle');
  }
  getReplace() {
    return this.modeler.get('replace');
  }
  getSelection() {
    return this.modeler.get('selection');
  }
  getEventBus() {
    return this.modeler.get('eventBus');
  }
  getShape(id: string) {
    return this.getElementRegistry().get(id);
  }

  updateActiveElement(data: any) {
    this.updateElement(this.getActiveElement(), data);
  }
  updateElement(element: any, data: any) {
    this.getModeling().updateProperties(element, data);
  }

  updateActiveModdleProperties(instance: any, data: any) {
    this.updateModdleProperties(this.getActiveElement(), instance, data);
  }
  updateModdleProperties(element: any, instance: any, data: any) {
    this.getModeling().updateModdleProperties(element, instance, data);
  }

  updateActiveElementExtensions(extensionList: any) {
    this.updateElementExtensions(this.getActiveElement(), extensionList);
  }
  updateElementExtensions(element: any, extensionList: any) {
    const extensions = this.getModdle().create(TaskName.extensionEl, {
      values: extensionList
    });
    this.updateElement(element, {
      extensionElements: extensions
    });
  }

  createListenerObject(options: any, isTask: boolean) {
    const listenerObj = Object.create(null);
    listenerObj.event = options.event;
    isTask && (listenerObj.id = options.id); // 任务监听器特有的 id 字段
    switch (options.listenerType) {
      case 'scriptListener':
        listenerObj.script = this.createScriptObject(options);
        break;
      case 'expressionListener':
        listenerObj.expression = options.expression;
        break;
      case 'delegateExpressionListener':
        listenerObj.delegateExpression = options.delegateExpression;
        break;
      default:
        listenerObj.class = options.class;
    }
    // 注入字段
    if (options.fields) {
      listenerObj.fields = options.fields.map((field: any) => {
        return this.createFieldObject(field);
      });
    }
    // 任务监听器的 定时器 设置
    if (isTask && options.event === 'timeout' && !!options.eventDefinitionType) {
      const timeDefinition = this.getModdle().create(TaskName.formalExpr, {
        body: options.eventTimeDefinitions
      });
      const TimerEventDefinition = this.getModdle().create(TaskName.timeEvent, {
        id: `TimerEventDefinition_${uuid(8)}`,
        [`time${options.eventDefinitionType.replace(/^\S/, (s: string) => s.toUpperCase())}`]:
          timeDefinition
      });
      listenerObj.eventDefinitions = [TimerEventDefinition];
    }
    return this.getModdle().create(
      `${isTask ? CamundaTaskName.taskListener : CamundaTaskName.exListener}`,
      listenerObj
    );
  }
  createFieldObject(option: any) {
    const { name, fieldType, string, expression } = option;
    const fieldConfig = fieldType === 'string' ? { name, string } : { name, expression };
    return this.getModdle().create(CamundaTaskName.field, fieldConfig);
  }
  createScriptObject(options: any) {
    const { scriptType, scriptFormat, value, resource } = options;
    const scriptConfig =
      scriptType === 'inlineScript' ? { scriptFormat, value } : { scriptFormat, resource };
    return this.getModdle().create(CamundaTaskName.script, scriptConfig);
  }
}
export const modeler = new FlowModeler();
